package bma.common.netty.thrift.dual.impl;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.apache.thrift.TApplicationException;
import org.apache.thrift.TBase;
import org.apache.thrift.TException;
import org.apache.thrift.protocol.TMessage;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.protocol.TProtocolException;
import org.apache.thrift.protocol.TProtocolUtil;
import org.apache.thrift.protocol.TType;

import bma.common.langutil.ai.stack.AIStackROOT;
import bma.common.netty.thrift.dual.NTSRequest;

public abstract class NTSBaseProcessor<IFACE> extends NTSModuleProcessor {

	final org.slf4j.Logger log = org.slf4j.LoggerFactory
			.getLogger(NTSBaseProcessor.class);

	protected final IFACE iface;
	protected Map<String, NTSProcessFunction<IFACE, ? extends TBase>> processMap;
	protected static ThreadLocal<NTSRequest> requestContent = new ThreadLocal<NTSRequest>();

	public static NTSRequest currentRequest() {
		return requestContent.get();
	}

	protected NTSBaseProcessor(IFACE iface) {
		this.iface = iface;
		this.processMap = createProcessFunctionMap();
	}

	protected Map createProcessFunctionMap() {
		return new HashMap();
	}

	public IFACE getIFace() {
		return iface;
	}

	public NTSProcessFunction<IFACE, ? extends TBase> queryProcessFunction(
			String name) {
		if (this.processMap == null)
			return null;
		return this.processMap.get(name);
	}

	public Set<String> listMethodsNames() {
		if (processMap == null)
			return Collections.emptySet();
		return processMap.keySet();
	}

	@Override
	public boolean process(NTSRequest req) {
		try {
			TMessage msg = req.getHeader();
			NTSProcessFunction fn = queryProcessFunction(msg.name);
			if (fn == null) {
				TProtocol in = req.getInputProtocol();
				TProtocolUtil.skip(in, TType.STRUCT);
				in.readMessageEnd();
				req.end(null, new TApplicationException(
						TApplicationException.UNKNOWN_METHOD,
						"Invalid method name: '" + msg.name + "'"));
				return true;
			}
			aiprocess(msg, req, fn);
		} catch (Exception e) {
			req.end(null, e);
		}
		return true;
	}

	protected void aiprocess(final TMessage msg, final NTSRequest req,
			NTSProcessFunction fn) throws TException {
		TBase args = fn.queryEmptyArgsInstance();
		TProtocol iprot = req.getInputProtocol();
		try {
			args.read(iprot);
		} catch (TProtocolException e) {
			iprot.readMessageEnd();
			req.end(null, new TApplicationException(
					TApplicationException.PROTOCOL_ERROR, e.getMessage()));
			return;
		}
		iprot.readMessageEnd();

		AIStackROOT<TBase> stack = new AIStackROOT<TBase>() {

			@Override
			public boolean end(TBase result, Throwable t) {
				if (t != null) {
					if (log.isDebugEnabled()) {
						log.debug("getResult fail", t);
					}
				}
				try {
					req.end(result, t);
				} catch (Exception e) {
					if (log.isWarnEnabled()) {
						log.warn("write response(" + result + ") fail", e);
					}
				}
				return true;
			}

		};
		requestContent.set(req);
		try {
			fn.getResult(stack, iface, args);
		} catch (Exception e) {
			if (log.isErrorEnabled()) {
				log.error("getResult (" + fn.getMethodName() + ") fail", e);
			}
			req.end(null, e);
		} finally {
			requestContent.set(null);
		}
	}
}
